package com.jjb.common.task.service;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jjb.common.task.enums.CompileStatusEnums;
import com.jjb.common.task.enums.SuccessStatusEnums;
import com.jjb.common.task.event.TaskEvent;
import com.jjb.common.task.infrastructure.persistence.domainobject.MessageTaskDO;
import com.jjb.common.task.infrastructure.persistence.mapper.MessageTaskMapper;
import com.jjb.common.task.util.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class TaskService implements ApplicationContextAware {

    private Logger log = LoggerFactory.getLogger(TaskService.class);

    private MessageTaskMapper messageTaskMapper;

    private TaskFactoryService taskFactoryService;
    private ApplicationContext applicationContext;

    public TaskService(MessageTaskMapper messageTaskMapper, TaskFactoryService taskFactoryService) {
        this.messageTaskMapper = messageTaskMapper;
        this.taskFactoryService = taskFactoryService;
    }

    public List<MessageTaskDO> findExec(int limit) {
        QueryWrapper<MessageTaskDO> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .eq("compile_status", CompileStatusEnums.NO.getStatus())
                .lt("next_exec_date", new Date());

        queryWrapper.last( " LIMIT 0, " + limit );
        return this.messageTaskMapper.selectList(queryWrapper);
    }

    @Transactional(rollbackFor = Exception.class, propagation= Propagation.REQUIRES_NEW)
    public void execById(long id) {
        QueryWrapper<MessageTaskDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", id)
                .eq("compile_status", CompileStatusEnums.NO.getStatus())
                .lt("next_exec_date", new Date());

        queryWrapper.last(" for update " );

        MessageTaskDO messageTask = this.messageTaskMapper.selectOne(queryWrapper);

        if( messageTask == null ) {
            return;
        }

        boolean bool = this.exec(messageTask);
        messageTask.setCurrentExecNum(messageTask.getCurrentExecNum() + 1 ); //执行次数+1
        if ( bool ) {
            messageTask.setCompileStatus(CompileStatusEnums.YES.getStatus()); //设置执行完成
            messageTask.setSuccessStatus(SuccessStatusEnums.SUCCESS.getStatus());//设置执行成功
        } else {
            if ( messageTask.getCurrentExecNum() >= messageTask.getTotalExecNum() ) {
                messageTask.setCompileStatus(CompileStatusEnums.YES.getStatus()); //设置执行完成
                messageTask.setSuccessStatus(SuccessStatusEnums.FAIL.getStatus());//设置执行成功
            } else {
                List<Long> intervals = Arrays.asList(messageTask.getIntervals().split(",")).stream().map(interval -> Long.parseLong(interval)).collect(Collectors.toList());
                messageTask.setNextExecDate(new Date(System.currentTimeMillis() + intervals.get(messageTask.getCurrentExecNum() - 1) ));
            }
        }

        this.messageTaskMapper.updateById(messageTask);
    }


    /**
     * 添加任务
     * @param t: 任务执行参数
     * @param type： 任务类型
     * @param intervals: 间隔时间
     */
    public void  addTask(Object t, String type, Long... intervals){

        if(  intervals == null || intervals.length == 0 ) {
            throw new RuntimeException("执行间隔时间不能为空");
        }

        MessageTaskDO messageTask = new MessageTaskDO();
        messageTask.setExecType(type);
        messageTask.setSuccessStatus(SuccessStatusEnums.MEANINGLESS.getStatus());
        messageTask.setCompileStatus(CompileStatusEnums.NO.getStatus());
        messageTask.setExecParam(JsonUtil.toJSONString(t));
        messageTask.setCurrentExecNum(0);
        messageTask.setTotalExecNum(intervals.length);
        messageTask.setNextExecDate(new Date(System.currentTimeMillis() - 1000000L));
        List<Long> intervalList = Arrays.asList(intervals);
        String intervalStr  = intervalList.stream().map(interval -> interval.toString()).collect(Collectors.joining(","));
        messageTask.setIntervals(intervalStr);
        this.messageTaskMapper.insert(messageTask);

        if ( applicationContext != null) {
            applicationContext.publishEvent(new TaskEvent(messageTask));
        }
    };

    @Transactional(rollbackFor = Exception.class, propagation= Propagation.REQUIRES_NEW)
    public  boolean exec(MessageTaskDO messageTask)  {
        try {
          return this.taskFactoryService.execHandler(messageTask.getExecType(), messageTask);
        }catch (Exception e) {
            log.error("执行任务失败:" + e.getMessage(), e);
            return false;
        }
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
